package com.milkcoffee.jvm.lesson01;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * 打破双亲委派机制
 *
 * @author zxs
 */
public class BreakParentalDelegationClassloader extends ClassLoader {
    private String classPath;

    private String fm = "%s/%s.class";

    public BreakParentalDelegationClassloader(String classPath) {
        this.classPath = classPath;
    }

    private byte[] findClassFileByte(String classFullName) throws IOException {

        //类全路径
        String classFullNamePath = classFullName.replaceAll("\\.", "/");
        String path = String.format(fm, classPath, classFullNamePath);

        FileInputStream stream = new FileInputStream(path);
        int len = stream.available();

        byte[] resultByte = new byte[len];
        stream.read(resultByte);
        stream.close();

        return resultByte;
    }

    /**
     * 在CustomizeClassloader中调用的loadclass的时候会调用这个方法
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {

            byte[] fileByte = findClassFileByte(name);
            return defineClass(name, fileByte, 0, fileByte.length);
        } catch (IOException e) {
            // 加载出错
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 首先检查当前类是否已经被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                if (name.startsWith("com.milkcoffee.jvm")) {
                    //查询当前目录下是否可以找到这个类
                    c = findClass(name);
                } else {
                    /**
                     *jdk自带的类依然由jvm的类加载器去加载
                     * 当前类是由appClassloader去加载的，除了自定义需要加载的类，其他的类依然使用的jvm的类加载器去加载
                     */
                    c = this.getClass().getClassLoader().loadClass(name);
                }

                long t1 = System.nanoTime();

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}
